__str__() 实现的正确自动生成也适用于 sqlalchemy 类?
Proper autogenerate of __str__() implementation also for sqlalchemy classes?
我想显示/打印我的 sqlalchemy class它很干净。
在中有没有办法在python中自动生成__str__()
实现?
答案 对简单的 class 有帮助。
当我尝试将它应用到 Sqlalchemy
class 时(比如
Python 的 SQLAlchemy 的入门教程 - 见下文),我得到 - 除了成员变量之外,还有以下条目作为成员变量:
_sa_instance_state=<sqlalchemy.orm.state.InstanceState object at 0x000000004CEBCC0>
如何避免此条目出现在 __str__
表示中?
为了完整起见,我也将链接的Whosebug问题的解决方案放在下面。
import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
# Here we define columns for the table person
# Notice that each column is also a normal Python instance attribute.
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
如前所述,这是 的解决方案:
def auto_str(cls):
def __str__(self):
return '%s(%s)' % (
type(self).__name__,
', '.join('%s=%s' % item for item in vars(self).items())
)
cls.__str__ = __str__
return cls
@auto_str
class Foo(object):
def __init__(self, value_1, value_2):
self.attribute_1 = value_1
self.attribute_2 = value_2
已申请:
>>> str(Foo('bar', 'ping'))
'Foo(attribute_2=ping, attribute_1=bar)'
这是我用的:
def todict(obj):
""" Return the object's dict excluding private attributes,
sqlalchemy state and relationship attributes.
"""
excl = ('_sa_adapter', '_sa_instance_state')
return {k: v for k, v in vars(obj).items() if not k.startswith('_') and
not any(hasattr(v, a) for a in excl)}
class Base:
def __repr__(self):
params = ', '.join(f'{k}={v}' for k, v in todict(self).items())
return f"{self.__class__.__name__}({params})"
Base = declarative_base(cls=Base)
任何继承自 Base
的模型都将定义默认的 __repr__()
方法,如果我需要做一些不同的事情,我可以覆盖特定 class 上的方法。
它排除了用前导下划线表示的任何私有属性的值、SQLAlchemy 实例状态对象以及字符串中的任何关系属性。我排除了关系属性,因为我通常不希望 repr 导致延迟加载关系,并且关系是双向的,包括关系属性会导致无限递归。
结果如下:ClassName(attr=val, ...)
.
--编辑--
我上面提到的 todict()
函数是一个助手,我经常调用它从 SQLA 对象构造一个 dict
,主要用于序列化。我在这种情况下懒洋洋地使用它,但它不是很有效,因为它正在构造一个 dict
(在 todict()
中)来构造一个 dict
(在 __repr__()
中)。我已经修改了模式以调用生成器:
def keyvalgen(obj):
""" Generate attr name/val pairs, filtering out SQLA attrs."""
excl = ('_sa_adapter', '_sa_instance_state')
for k, v in vars(obj).items():
if not k.startswith('_') and not any(hasattr(v, a) for a in excl):
yield k, v
那么基础Base是这样的:
class Base:
def __repr__(self):
params = ', '.join(f'{k}={v}' for k, v in keyvalgen(self))
return f"{self.__class__.__name__}({params})"
todict()
函数也利用了 keyvalgen()
生成器,但不再需要构建 repr。
我在我的基础模型上定义了这个 __repr__
方法:
def __repr__(self):
fmt = '{}.{}({})'
package = self.__class__.__module__
class_ = self.__class__.__name__
attrs = sorted(
(k, getattr(self, k)) for k in self.__mapper__.columns.keys()
)
sattrs = ', '.join('{}={!r}'.format(*x) for x in attrs)
return fmt.format(package, class_, sattrs)
该方法按字母顺序显示 table 的所有列(但不是关系)的名称及其值的 repr
。我通常不定义 __str__
除非我需要一个特定的形式 - 也许 str(User(name='Alice'))
只是 Alice
- 所以 str(model_instance)
将调用 __repr__
方法。
示例代码
import datetime
import sqlalchemy as sa
from sqlalchemy.ext import declarative
class BaseModel(object):
__abstract__ = True
def __repr__(self):
fmt = u'{}.{}({})'
package = self.__class__.__module__
class_ = self.__class__.__name__
attrs = sorted(
(k, getattr(self, k)) for k in self.__mapper__.columns.keys()
)
sattrs = u', '.join('{}={!r}'.format(*x) for x in attrs)
return fmt.format(package, class_, sattrs)
Base = declarative.declarative_base(cls=BaseModel)
class MyModel(Base):
__tablename__ = 'mytable'
foo = sa.Column(sa.Unicode(32))
bar = sa.Column('bar_id', sa.Integer, primary_key=True)
baz = sa.Column(sa.DateTime)
>>> mm = models.MyModel(foo='Foo', bar=42, baz=datetime.datetime.now())
>>> mm
models.MyModel(bar=42, baz=datetime.datetime(2019, 1, 4, 7, 37, 59, 350432), foo='Foo')
此答案的原始版本使用模型的 __table__
属性来访问列名。我已将其更改为使用模型的 __mapper__
,因为 SQLAlchemy 允许模型属性名称(存储在映射器中)与数据库中的列名称(存储在 table 中)不同。 MyModel.bar
证明了这一点。
我想显示/打印我的 sqlalchemy class它很干净。
在中有没有办法在python中自动生成__str__()
实现?
答案
当我尝试将它应用到 Sqlalchemy
class 时(比如
Python 的 SQLAlchemy 的入门教程 - 见下文),我得到 - 除了成员变量之外,还有以下条目作为成员变量:
_sa_instance_state=<sqlalchemy.orm.state.InstanceState object at 0x000000004CEBCC0>
如何避免此条目出现在 __str__
表示中?
为了完整起见,我也将链接的Whosebug问题的解决方案放在下面。
import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
# Here we define columns for the table person
# Notice that each column is also a normal Python instance attribute.
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
如前所述,这是
def auto_str(cls):
def __str__(self):
return '%s(%s)' % (
type(self).__name__,
', '.join('%s=%s' % item for item in vars(self).items())
)
cls.__str__ = __str__
return cls
@auto_str
class Foo(object):
def __init__(self, value_1, value_2):
self.attribute_1 = value_1
self.attribute_2 = value_2
已申请:
>>> str(Foo('bar', 'ping'))
'Foo(attribute_2=ping, attribute_1=bar)'
这是我用的:
def todict(obj):
""" Return the object's dict excluding private attributes,
sqlalchemy state and relationship attributes.
"""
excl = ('_sa_adapter', '_sa_instance_state')
return {k: v for k, v in vars(obj).items() if not k.startswith('_') and
not any(hasattr(v, a) for a in excl)}
class Base:
def __repr__(self):
params = ', '.join(f'{k}={v}' for k, v in todict(self).items())
return f"{self.__class__.__name__}({params})"
Base = declarative_base(cls=Base)
任何继承自 Base
的模型都将定义默认的 __repr__()
方法,如果我需要做一些不同的事情,我可以覆盖特定 class 上的方法。
它排除了用前导下划线表示的任何私有属性的值、SQLAlchemy 实例状态对象以及字符串中的任何关系属性。我排除了关系属性,因为我通常不希望 repr 导致延迟加载关系,并且关系是双向的,包括关系属性会导致无限递归。
结果如下:ClassName(attr=val, ...)
.
--编辑--
我上面提到的 todict()
函数是一个助手,我经常调用它从 SQLA 对象构造一个 dict
,主要用于序列化。我在这种情况下懒洋洋地使用它,但它不是很有效,因为它正在构造一个 dict
(在 todict()
中)来构造一个 dict
(在 __repr__()
中)。我已经修改了模式以调用生成器:
def keyvalgen(obj):
""" Generate attr name/val pairs, filtering out SQLA attrs."""
excl = ('_sa_adapter', '_sa_instance_state')
for k, v in vars(obj).items():
if not k.startswith('_') and not any(hasattr(v, a) for a in excl):
yield k, v
那么基础Base是这样的:
class Base:
def __repr__(self):
params = ', '.join(f'{k}={v}' for k, v in keyvalgen(self))
return f"{self.__class__.__name__}({params})"
todict()
函数也利用了 keyvalgen()
生成器,但不再需要构建 repr。
我在我的基础模型上定义了这个 __repr__
方法:
def __repr__(self):
fmt = '{}.{}({})'
package = self.__class__.__module__
class_ = self.__class__.__name__
attrs = sorted(
(k, getattr(self, k)) for k in self.__mapper__.columns.keys()
)
sattrs = ', '.join('{}={!r}'.format(*x) for x in attrs)
return fmt.format(package, class_, sattrs)
该方法按字母顺序显示 table 的所有列(但不是关系)的名称及其值的 repr
。我通常不定义 __str__
除非我需要一个特定的形式 - 也许 str(User(name='Alice'))
只是 Alice
- 所以 str(model_instance)
将调用 __repr__
方法。
示例代码
import datetime
import sqlalchemy as sa
from sqlalchemy.ext import declarative
class BaseModel(object):
__abstract__ = True
def __repr__(self):
fmt = u'{}.{}({})'
package = self.__class__.__module__
class_ = self.__class__.__name__
attrs = sorted(
(k, getattr(self, k)) for k in self.__mapper__.columns.keys()
)
sattrs = u', '.join('{}={!r}'.format(*x) for x in attrs)
return fmt.format(package, class_, sattrs)
Base = declarative.declarative_base(cls=BaseModel)
class MyModel(Base):
__tablename__ = 'mytable'
foo = sa.Column(sa.Unicode(32))
bar = sa.Column('bar_id', sa.Integer, primary_key=True)
baz = sa.Column(sa.DateTime)
>>> mm = models.MyModel(foo='Foo', bar=42, baz=datetime.datetime.now())
>>> mm
models.MyModel(bar=42, baz=datetime.datetime(2019, 1, 4, 7, 37, 59, 350432), foo='Foo')
此答案的原始版本使用模型的 __table__
属性来访问列名。我已将其更改为使用模型的 __mapper__
,因为 SQLAlchemy 允许模型属性名称(存储在映射器中)与数据库中的列名称(存储在 table 中)不同。 MyModel.bar
证明了这一点。